#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "../include/transportADT.h"

//valor minimo de puerto que voy a sumar al id que me pasen para no caer
//dentro de los valores reservados o de los mas usados (<= 1024)
#define MIN_PORT_NUM 2000

struct transportCDT{
	int sockfd;						//file descriptor del socket
	struct sockaddr_in destAddr;	//direccion de destino del mensaje
	socklen_t destLength;			//tamaño de la direccion de destino
};

static struct transportCDT * prepareSock(struct sockaddr_in myAddr);

transportADT
openTrans(void * id){
	struct sockaddr_in myAddr;

	//seteo que voy a usar la familia inet, en cualquier direccion ip, en el
	//puerto indicado por el id + el puerto minimo
	myAddr.sin_family = AF_INET;
	myAddr.sin_addr.s_addr = INADDR_ANY;
	myAddr.sin_port = *((int *)id) + MIN_PORT_NUM;

	struct transportCDT * trans;

	trans = prepareSock(myAddr);

	//pongo el tamaño de la estructura de destino
	trans->destLength = sizeof(struct sockaddr_in);

	return trans;
}

transportADT
getTrans(void * id, void * dest){
	struct transportCDT * trans;

	trans = openTrans(id);

	//pongo la direccion de destino pasada
	trans->destAddr.sin_family = AF_INET;
	trans->destAddr.sin_port = *((int *)dest) + MIN_PORT_NUM;
	trans->destAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	return trans;
}

static struct transportCDT *
prepareSock(struct sockaddr_in myAddr) {
	int err;
	struct transportCDT * trans;

	if((trans=malloc(sizeof(struct transportCDT)))==NULL)
		exit(1);

	//creo un nuevo socket con el protocolo udp (datagrama)
	trans->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(trans->sockfd == -1) {
		fprintf(stderr, "no se pudo abrir el socket\n");
		exit(1);
	}

	//hago bind del socket en la direccion recibida por parametro
	err = bind(trans->sockfd, (struct sockaddr *)&myAddr,
		sizeof(struct sockaddr_in));

	if (err == -1) {
		fprintf(stderr, "No se pudo hacer bind con el socket\n");
		exit(1);
	}

	return trans;
}

int
readMsg(transportADT trans, void * buf, int size){
	int cant;

	cant = recvfrom(trans->sockfd, buf, size, 0,
		(struct sockaddr *)&(trans->destAddr), &(trans->destLength));

	return cant;
}

int
writeMsg(transportADT trans, const void * buf, int size){
	int cant;

	cant = sendto(trans->sockfd, buf, size, 0,
		(struct sockaddr *)&(trans->destAddr), trans->destLength);

	return cant;
}

void
closeTrans(transportADT trans){
	if(trans!=NULL) {
		close(trans->sockfd);
		free(trans);
	}
}
